<script setup lang="ts">
import { YiAction } from '@uublue/yimi-element-plus'
import { ElCheckboxGroup } from 'element-plus'
import InherentPermissionDisplay from './inherent-permission-display.vue'
import type { ApiConfigFun } from '@/utils/api'
import type { AxiosRequestConfig } from 'axios'
import type { PropType, Ref } from 'vue'
import { ref, watch } from 'vue'
import { actionAxios } from '@/utils/request'
import { matchRoles, matchPermissions, matchPermission } from '@/utils/resource-security'

const props = defineProps({
  api: {
    type: [Object, Function] as PropType<ApiConfigFun | AxiosRequestConfig>,
    required: true
  },
  roleSecurityApi: {
    type: [Object, Function] as PropType<ApiConfigFun | AxiosRequestConfig>,
    required: true
  },
  currentUser: {
    type: Object as PropType<Record<string, any>>,
    required: true
  },
  currentResource: {
    type: Object as PropType<Record<string, any>>,
    required: true
  }
})

// define emits
const emit = defineEmits<{
  (e: 'onSubmitSuccess', res: any, model: Record<string, any>): void
}>()

// on create
let initSteps = 0
let openSteps = 0
const modelValue: Ref<boolean | undefined> = ref()
const directEdit: Ref<boolean> = ref(true)

const checkedRoles: Ref<string[]> = ref([])
const checkedPermissions: Ref<string[]> = ref([])
const scopeRoles: string[] = props.currentResource.roleRequirement?.codes || []
const scopePermissions: string[] = props.currentResource.permissionRequirement?.codes || []

const rolePermissionDictionary: {
  code: string
  permissions: string[]
  defaultPermissions: string[]
}[] = props.currentUser.roles

const checkedOwnedRoles: Ref<string[]> = ref([])
const checkedOwnedPermissions: Ref<string[]> = ref([])
const scopeOwnedRoles: Ref<string[]> = ref([])
const scopeOwnedPermissions: Ref<string[]> = ref([])

const inherentPermissions: Ref<
  Record<
    string,
    {
      roles: string[]
      permissions: string[]
    }
  >
> = ref({})

function initContext() {
  
  inherentPermissions.value = {}

  checkedRoles.value = []
  checkedPermissions.value = []

  checkedOwnedRoles.value = []
  scopeOwnedRoles.value = []

  checkedOwnedPermissions.value = []
  scopeOwnedPermissions.value = []

  

  if (scopePermissions.length > 0) {
    props.currentUser.ownedRoles.forEach((roleCode: string) => {
      if (!scopeRoles.includes(roleCode)) {
        const role = rolePermissionDictionary.find((role) => role.code == roleCode)
        if (role) {
          ;(role.permissions || [])
            .concat(role.defaultPermissions || [])
            .forEach((permission: string) => {
              scopePermissions.forEach((_permission: string) => {
                if (matchPermission(permission, _permission)) {
                  insurePermissionFromRole(_permission, roleCode)

                  insureOwnedRoleChecked(roleCode)
                }
              })
            })
        }
      } else {
        checkedRoles.value.push(roleCode)
      }
    })

    props.currentUser.ownedPermissions.forEach((ownedPermission: string) => {
      if (scopePermissions.includes(ownedPermission)) {
        checkedPermissions.value.push(ownedPermission)
      } else {
        scopePermissions.forEach((_permission: string) => {
          if (matchPermission(ownedPermission, _permission)) {
            insurePermissionFromPermission(_permission, ownedPermission)
            insureOwnedPermissionChecked(ownedPermission)
          }
        })
      }
    })
  }


  directEdit.value = true
  if (scopeRoles.length > 0) {
    directEdit.value = false
  } else if (scopeOwnedRoles.value.length > 0) {
    directEdit.value = false
  } else if (scopeOwnedPermissions.value.length > 0) {
    directEdit.value = false
  } else if (scopePermissions.length > 1) {
    directEdit.value = false
  }

  initSteps++
}

// 确保权限来自角色
function insurePermissionFromRole(permission: string, role: string) {
  let inherentPermission = inherentPermissions.value[permission]
  if (!inherentPermission) {
    inherentPermission = {
      roles: [],
      permissions: []
    }
    inherentPermissions.value[permission] = inherentPermission
  }
  if (!inherentPermission.roles.includes(role)) {
    inherentPermission.roles.push(role)
  }
}

function insurePermissionNotFromRole(permission: string, role: string) {
  let inherentPermission = inherentPermissions.value[permission]
  if (inherentPermission) {
    const roleIndex = inherentPermission.roles.findIndex((_role) => _role == role)
    if (roleIndex != -1) {
      inherentPermission.roles.splice(roleIndex, 1)

      if (inherentPermission.roles.length == 0 && inherentPermission.permissions.length == 0) {
        delete inherentPermissions.value[permission]
      }
    }
  }
}

//确保权限来自权限
function insurePermissionFromPermission(permission: string, parentPermission: string) {
  let inherentPermission = inherentPermissions.value[permission]
  if (!inherentPermission) {
    inherentPermission = {
      roles: [],
      permissions: []
    }
    inherentPermissions.value[permission] = inherentPermission
  }
  if (!inherentPermission.permissions.includes(parentPermission)) {
    inherentPermission.permissions.push(parentPermission)
  }
}

function insurePermissionNotFromPermission(permission: string, parentPermission: string) {
  let inherentPermission = inherentPermissions.value[permission]
  if (inherentPermission) {
    const permissionIndex = inherentPermission.permissions.findIndex(
      (_permission) => _permission == parentPermission
    )
    if (permissionIndex != -1) {
      inherentPermission.permissions.splice(permissionIndex, 1)

      if (inherentPermission.roles.length == 0 && inherentPermission.permissions.length == 0) {
        delete inherentPermissions.value[permission]
      }
    }
  }
}

//确保相关自有角色选中
function insureOwnedRoleChecked(ownedRole: string) {
  if (!scopeOwnedRoles.value.includes(ownedRole)) {
    scopeOwnedRoles.value.push(ownedRole)
  }
  if (!checkedOwnedRoles.value.includes(ownedRole)) {
    checkedOwnedRoles.value.push(ownedRole)
  }
}

//确保相关自有权限选中
function insureOwnedPermissionChecked(ownedPermission: string) {
  if (!scopeOwnedPermissions.value.includes(ownedPermission)) {
    scopeOwnedPermissions.value.push(ownedPermission)
  }
  if (!checkedOwnedPermissions.value.includes(ownedPermission)) {
    checkedOwnedPermissions.value.push(ownedPermission)
  }
}

/**
 * 如果该权限在资源权限列表中，
 * 则确保该权限在资源权限列表中被选中
 * @param permission 要选中的资源权限
 */
function insurePermissionChecked(permission: string) {
  if (scopePermissions.includes(permission) && !checkedPermissions.value.includes(permission)) {
    checkedPermissions.value.push(permission)
  }
}

watch(checkedOwnedPermissions, () => {
  scopeOwnedPermissions.value.forEach((scopeOwnedPermission) => {
    if (checkedOwnedPermissions.value.includes(scopeOwnedPermission)) {
      scopePermissions.forEach((_permission: string) => {
        if (matchPermission(scopeOwnedPermission, _permission)) {
          insurePermissionFromPermission(_permission, scopeOwnedPermission)
          insurePermissionChecked(_permission)
        }
      })
    } else {
      scopePermissions.forEach((_permission: string) => {
        insurePermissionNotFromPermission(_permission, scopeOwnedPermission)
      })
    }
  })
})

watch(checkedOwnedRoles, () => {
  scopeOwnedRoles.value.forEach((scopeOwnedRole) => {
    const role = rolePermissionDictionary.find((role) => role.code == scopeOwnedRole)
    if (role) {
      if (checkedOwnedRoles.value.includes(scopeOwnedRole)) {
        ;(role.permissions || [])
          .concat(role.defaultPermissions || [])
          .forEach((permission: string) => {
            scopeOwnedPermissions.value.forEach((_permission: string) => {
              if (matchPermission(permission, _permission)) {
                insurePermissionFromRole(_permission, scopeOwnedRole)
                insureOwnedPermissionChecked(_permission)

                scopePermissions.forEach((__permission: string) => {
                  if (matchPermission(_permission, __permission)) {
                    insurePermissionFromPermission(__permission, _permission)
                    insurePermissionChecked(__permission)
                  }
                })
              }
            })
            scopePermissions.forEach((_permission: string) => {
              if (matchPermission(permission, _permission)) {
                insurePermissionFromRole(_permission, scopeOwnedRole)
                insurePermissionChecked(_permission)
              }
            })
          })
      } else {
        ;(role.permissions || [])
          .concat(role.defaultPermissions || [])
          .forEach((permission: string) => {
            scopeOwnedPermissions.value.forEach((_permission: string) => {
              if (matchPermission(permission, _permission)) {
                insurePermissionNotFromRole(_permission, scopeOwnedRole)
              }
            })

            scopePermissions.forEach((_permission: string) => {
              if (matchPermission(permission, _permission)) {
                insurePermissionNotFromRole(_permission, scopeOwnedRole)
              }
            })
          })
      }
    }
  })
})

watch(checkedRoles, () => {
  scopeRoles.forEach((scopeRole: string) => {
    const role = rolePermissionDictionary.find((role) => role.code == scopeRole)
    if (role) {
      if (checkedRoles.value.includes(scopeRole)) {
        ;(role.permissions || [])
          .concat(role.defaultPermissions || [])
          .forEach((permission: string) => {
            scopePermissions.forEach((_permission: string) => {
              if (matchPermission(permission, _permission)) {
                insurePermissionFromRole(_permission, scopeRole)
                insurePermissionChecked(_permission)
              }
            })
          })
      } else {
        ;(role.permissions || [])
          .concat(role.defaultPermissions || [])
          .forEach((permission: string) => {
            scopePermissions.forEach((_permission: string) => {
              if (matchPermission(permission, _permission)) {
                insurePermissionNotFromRole(_permission, scopeRole)
              }
            })
          })
      }
    }
  })
})

//监听用户权限变化
watch(
  () => props.currentUser,
  () => {
    const roleMatched = matchRoles(
      props.currentUser.ownedRoles,
      props.currentResource.roleRequirement
    )
    const permissionMatchResult = matchPermissions(
      [],
      props.currentUser.allPermissions,
      props.currentResource.permissionRequirement
    )

    modelValue.value = roleMatched && permissionMatchResult.matched

    initContext()
  },
  { immediate: true }
)

function buildRequestData(): Record<string, any> {
  const requestData: Record<string, any> = {
    userId: props.currentUser.id
  }
  if (directEdit.value) {
    requestData.scopeRoles = scopeRoles
    requestData.scopePermissions = scopePermissions

    requestData.checkedRoles = modelValue.value ? scopeRoles : []
    requestData.checkedPermissions = modelValue.value ? scopePermissions : []
  } else {
    requestData.scopeRoles = scopeRoles.concat(scopeOwnedRoles.value)
    requestData.scopePermissions = scopePermissions
      .concat(scopeOwnedPermissions.value)
      .filter((permission) => {
        return inherentPermissions.value[permission] == undefined
      })

    requestData.checkedRoles = checkedRoles.value.concat(checkedOwnedRoles.value)
    requestData.checkedPermissions = checkedPermissions.value
      .concat(checkedOwnedPermissions.value)
      .filter((permission) => {
        return inherentPermissions.value[permission] == undefined
      })
  }

  return requestData
}
</script>

<template>
  <yi-action
    v-model="modelValue"
    :api="api"
    :on-open="
      ({ resolve }) => {
        openSteps++
        if (openSteps > initSteps) {
          initContext()
        }
        const roles = scopeRoles.filter((scopeRole: string) => {
          return currentUser.roles.find((role: any) => role.code == scopeRole) == undefined
        })
        if (roles.length > 0) {
          actionAxios.request((roleSecurityApi as ApiConfigFun)(roles)).then((res) => {
            ;(res.data as any[]).forEach((dic) => {
              rolePermissionDictionary.push(dic)
            })
            resolve()
          })
        } else {
          resolve()
        }
      }
    "
    :on-submit="
      (scope, request, close) => {
        request({ data: buildRequestData() }).then(() => {
          close(true)
        })
      }
    "
    :modal-title="'【' + currentResource.name + '】'"
    trigger="switch"
    model-reserve="never"
    @on-submit-success="
      (res, model) => {
        emit('onSubmitSuccess', res, model)
      }
    "
  >
    <template v-if="!directEdit" #default>
      <el-form label-width="150px" class="security-action-form">
        <el-divider>资源所需</el-divider>
        <el-form-item
          v-if="
            currentResource.roleRequirement &&
            Array.isArray(currentResource.roleRequirement.codes) &&
            currentResource.roleRequirement.codes.length > 0
          "
          :label="
            '所需角色' +
            (currentResource.roleRequirement.logic
              ? '(' + currentResource.roleRequirement.logic + ')'
              : '')
          "
        >
          <el-checkbox-group v-model="checkedRoles">
            <el-checkbox
              v-for="(code, index) in scopeRoles"
              :key="index"
              :value="code"
              :label="code"
            ></el-checkbox>
          </el-checkbox-group>
        </el-form-item>

        <el-form-item
          v-if="
            currentResource.permissionRequirement &&
            Array.isArray(currentResource.permissionRequirement.codes)
          "
          :label="
            '所需权限' +
            (currentResource.permissionRequirement.logic
              ? '(' + currentResource.permissionRequirement.logic + ')'
              : '')
          "
        >
          <el-checkbox-group v-model="checkedPermissions">
            <template v-for="(code, index) in scopePermissions" :key="index">
              <el-tooltip
                v-if="inherentPermissions[code] != undefined"
                placement="right"
                effect="light"
              >
                <template #default>
                  <el-checkbox :value="code" :label="code" disabled></el-checkbox>
                </template>

                <template #content>
                  <inherent-permission-display :data="inherentPermissions[code]" />
                </template>
              </el-tooltip>
              <el-checkbox v-else :value="code" :label="code"></el-checkbox>
            </template>
          </el-checkbox-group>
        </el-form-item>

        <el-divider v-if="scopeOwnedRoles.length > 0 || scopeOwnedPermissions.length > 0"
          >已有相关</el-divider
        >

        <el-form-item v-if="scopeOwnedRoles.length > 0" label="已有角色">
          <el-checkbox-group v-model="checkedOwnedRoles">
            <el-checkbox
              v-for="(code, index) in scopeOwnedRoles"
              :key="index"
              :value="code"
              :label="code"
            ></el-checkbox>
          </el-checkbox-group>
        </el-form-item>

        <el-form-item v-if="scopeOwnedPermissions.length > 0" label="已有权限">
          <el-checkbox-group v-model="checkedOwnedPermissions">
            <template v-for="(code, index) in scopeOwnedPermissions" :key="index">
              <el-tooltip
                v-if="inherentPermissions[code] != undefined"
                placement="bottom"
                effect="light"
              >
                <template #default>
                  <el-checkbox :value="code" :label="code" disabled></el-checkbox>
                </template>

                <template #content>
                  <inherent-permission-display :data="inherentPermissions[code]" />
                </template>
              </el-tooltip>
              <el-checkbox v-else :value="code" :label="code"></el-checkbox>
            </template>
          </el-checkbox-group>
        </el-form-item>
      </el-form>
    </template>
  </yi-action>
</template>
